using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using System.Linq;
using System.Collections.ObjectModel;
using CoolEngine.Engine;

namespace CoolEngine.Gui
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class GuiRenderer : DrawableGameComponent
    {
        public bool DesignMode { get; private set; }
        internal SortedList<int, GuiControl> controls = new SortedList<int, GuiControl>();
        private ReadOnlyCollection<GuiControl> readonlyControls;
        public ReadOnlyCollection<GuiControl> Controls { get { return readonlyControls; } }
        public SpriteBatch Batch { get; private set; }
        public bool RenderGameScreenOnly { get; set; }
        public GuiRenderer()
            : this(null, true)
        {

        }
        public GuiRenderer(Game game)
            : base(game)
        {
            if (game == null)
            {
                DesignMode = true;
            }
        }
        public GuiRenderer(Game game, bool designMode)
            : base(game)
        {
            DesignMode = designMode;
        }

        public void AddControl(GuiControl control)
        {            
            controls.Add(control.DrawOrder, control);
        }
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            readonlyControls = new ReadOnlyCollection<GuiControl>(controls.Values);
            if (!DesignMode)
            {
                Batch = new SpriteBatch(Game.GraphicsDevice);
            }
            foreach (GuiControl control in Controls)
            {
                control.Initialize();
            }

            base.Initialize();
        }
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (RenderGameScreenOnly)
            {
                DoForEachVisibleScreen(gameTime, gs => gs.FullScreen, (gs, gt) => gs.Update(gt));
            }
            else
            {
                foreach (GuiControl control in Controls)
                {
                    if (control.Enabled)
                    {
                        control.Update(gameTime);
                    }
                }
            }
            base.Update(gameTime);
        }

        
        public override void Draw(GameTime gameTime)
        {
            bool oldScissorTest = GraphicsDevice.RenderState.ScissorTestEnable;
            Rectangle oldScissorRect = GraphicsDevice.ScissorRectangle;
            GraphicsDevice.RenderState.ScissorTestEnable = true;
            Batch.Begin();
            if (RenderGameScreenOnly)
            {
                DoForEachVisibleScreen(gameTime, gs=> gs.FullScreen, (gs,gt)=>gs.Render(gt));
                

            }
            else
            {
                //Draw each visible control
                foreach (GuiControl control in Controls)
                {
                    if (control.Visible)
                    {
                        control.Render(gameTime);
                    }
                }
            }
            Batch.End();
            if (oldScissorTest == false)
            {
                GraphicsDevice.RenderState.ScissorTestEnable = oldScissorTest;
            }
            else
            {
                GraphicsDevice.ScissorRectangle = oldScissorRect;
            }
            base.Draw(gameTime);
        }

        private static void DoForEachVisibleScreen(GameTime gameTime, Func<GameScreen, bool> stopAt, Action<GameScreen, GameTime> action)
        {
            Stack<GameScreen> stack = new Stack<GameScreen>();
            GameScreen control = GameManager.GameState.CurrentScreen;
            if (stopAt(control))
            {
                action(control, gameTime);
            }
            else
            {
                while (!stopAt(GameManager.GameState.CurrentScreen))
                {
                    stack.Push(GameManager.GameState.Screens.Pop());
                }
                while (stack.Count > 0)
                {
                    GameScreen screen = stack.Pop();
                    action(screen, gameTime);
                    GameManager.GameState.Screens.Push(screen);
                }
            }
        }

    }
}